package org.summer.web.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.stereotype.Service;
import org.summer.common.core.constant.TenantConstants;
import org.summer.common.core.constant.UserConstants;
import org.summer.common.core.domain.model.MiniAppLoginUser;
import org.summer.common.core.enums.LoginType;
import org.summer.common.core.enums.UserStatus;
import org.summer.common.core.enums.UserType;
import org.summer.common.core.exception.ServiceException;
import org.summer.common.core.utils.ValidatorUtils;
import org.summer.common.json.utils.JsonUtils;
import org.summer.common.satoken.utils.LoginHelper;
import org.summer.lawyer.constant.SystemConstants;
import org.summer.lawyer.domain.bo.brokerageuser.BrokerageUserBo;
import org.summer.lawyer.domain.bo.nimaccount.NimAccountBo;
import org.summer.lawyer.domain.vo.nimaccount.NimAccountVo;
import org.summer.lawyer.domain.vo.wxuser.WxUserCreateVo;
import org.summer.lawyer.domain.vo.wxuser.WxUserVo;
import org.summer.lawyer.service.IBrokerageUserService;
import org.summer.lawyer.service.INimAccountService;
import org.summer.lawyer.service.IWxUserService;
import org.summer.lawyer.util.IdWorkerUtil;
import org.summer.system.domain.vo.SysClientVo;
import org.summer.web.domain.vo.LoginVo;
import org.summer.web.domain.vo.MiniAppLoginBodyVo;
import org.summer.web.service.IAuthStrategy;
import org.summer.web.service.SysLoginService;

/**
 * 小程序认证策略
 *
 * @author Michelle.Chung
 */
@Slf4j
@Service("miniapp" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class MiniAppAuthStrategy implements IAuthStrategy {

    private final SysLoginService loginService;

    private final IWxUserService wxUserService;

    private final WxMaService wxMaService;

    private final INimAccountService nimAccountService;

    private final IBrokerageUserService brokerageUserService;


    /**
     * 小程序一键登录
     *
     */
    @Override
    public LoginVo login(String body, SysClientVo client) {
        MiniAppLoginBodyVo loginBody = JsonUtils.parseObject(body, MiniAppLoginBodyVo.class);
          ValidatorUtils.validate(loginBody);
        // 多个小程序识别使用
        String appid = loginBody.getAppId();
        String code = loginBody.getCode();
        String openId = loginBody.getOpenId();
        String username = loginBody.getUsername();
        String password = loginBody.getPassword();
        String brokerageCode = loginBody.getBrokerageCode();
        WxMaUserInfo userInfo = null;
        WxMaPhoneNumberInfo phoneNumberInfo = null;
        WxUserVo user = null;
        String phoneNumber = "";
        if(StrUtil.isNotEmpty(username) && StrUtil.isNotEmpty(password)){
            //使用密码认证
            user = loadUserByUserName(username);
            WxUserVo finalUser = user;
            loginService.checkLogin(LoginType.PASSWORD, TenantConstants.DEFAULT_TENANT_ID,username,() -> !BCrypt.checkpw(password, finalUser.getPassword()));
        }else{
            if (StrUtil.isEmpty(appid)) {
                throw new ServiceException("APPID不能为空");
            }
            if(code.length() == 11){
                //模拟登录
                user = loadUserByPhoneNumber(code);
            }else{
                try {
                    //解密用户手机号
                    wxMaService.switchoverTo(appid);
                    phoneNumberInfo = wxMaService.getUserService().getPhoneNumber(code);
                    phoneNumber = phoneNumberInfo.getPurePhoneNumber();
                    user = loadUserByPhoneNumber(phoneNumber);
                } catch (WxErrorException ex) {
                    log.error("获取微信手机号发生错误,{},错误信息：{}", openId, ex.getMessage());
                    throw new ServiceException("获取手机号信息发生错误");
                }
            }
        }

        //更新用户openid
        user.setOpenId(openId);
        wxUserService.updateWxUserOpenId(user);
        //获取NIM账号信息
        NimAccountBo nimAccountBo = new NimAccountBo();
        nimAccountBo.setNimType(SystemConstants.NIM_MINIAPP_USER);
        nimAccountBo.setOpenId(openId);
        nimAccountBo.setNickname(user.getNickname());
        nimAccountBo.setAvatar(user.getAvatar());
        NimAccountVo nimAccountVo = nimAccountService.loadNimAccountVo(nimAccountBo);
        //创建分销用户
        if(StrUtil.isNotEmpty(brokerageCode)){
            BrokerageUserBo brokerageUserBo = new BrokerageUserBo();
            brokerageUserBo.setWxUserId(user.getWxUserId());
            brokerageUserBo.setBrokerageCode(brokerageCode);
            brokerageUserService.bindBrokerageUser(brokerageUserBo);
        }
        MiniAppLoginUser loginUser = new MiniAppLoginUser();
        loginUser.setTenantId(TenantConstants.DEFAULT_TENANT_ID);
        loginUser.setUserId(user.getWxUserId());
        loginUser.setUsername(user.getAccount());
        loginUser.setNickname(user.getNickname());
        loginUser.setUserType(UserType.MINI_APP_USER.getUserType());
        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());
        loginUser.setOpenId(user.getOpenId());

        SaLoginModel model = new SaLoginModel();
        model.setDevice(client.getDeviceType());
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
        // 例如: 后台用户30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // 生成token
        LoginHelper.login(loginUser, model);
        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());
        loginVo.setOpenId(openId);
        loginVo.setUserId(user.getWxUserId());
        return loginVo;
    }

    private WxUserVo loadUserByPhoneNumber(String phoneNumber) {
        // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
        WxUserVo vo = wxUserService.selectByPhoneNumber(phoneNumber, SystemConstants.WX_USER_TYPE_NORMAL);
        if (ObjectUtil.isNull(vo)) {
           return buildWxUser(phoneNumber, SystemConstants.WX_USER_TYPE_NORMAL);
        }
        if (UserStatus.DISABLE.getCode().equals(vo.getStatus())) {
            log.info("登录用户：{} 已被停用.", vo.getPhoneNumber());
            throw new ServiceException("对不起，您的账号已停用，请联系管理员");
        }
        return vo;
    }

    private WxUserVo loadUserByUserName(String userName){
        WxUserVo vo = wxUserService.selectWxUserByAccount(userName);
        if(ObjectUtil.isNull(vo)){
            throw new ServiceException(StrUtil.format("登录用户：{} 不存在", userName));
        }
        if(UserStatus.DISABLE.getCode().equals(vo.getStatus())){
            throw new ServiceException(StrUtil.format("登录用户：{} 已被停用，请联系管理员", userName));
        }
        return vo;
    }

    private WxUserVo buildWxUser(String phoneNumber, String wxUserType) {
        WxUserCreateVo wxUserCreateVo = new WxUserCreateVo();
        wxUserCreateVo.setPassword(BCrypt.hashpw(IdUtil.nanoId()));
        wxUserCreateVo.setNickname(getNickName());
        wxUserCreateVo.setPhoneNumber(phoneNumber);
        wxUserCreateVo.setSex(UserConstants.USER_GENDER_MALE);
        wxUserCreateVo.setStatus(UserConstants.USER_NORMAL);
        wxUserCreateVo.setWxUserType(wxUserType);
        wxUserService.saveByPhoneNumber(wxUserCreateVo);
        return wxUserService.selectByPhoneNumber(phoneNumber, wxUserType);
    }

    private String getNickName(){
        return StrUtil.format("用户{}", IdWorkerUtil.nextIdStr());
    }

}
